import java_cup.runtime.*;
import java.util.regex.*;
import java_cup.sym;

%%

//%standalone
%class LexicalAnalyzer
%column
%line
%cupsym SymbolsTable
%cup

%{

	private StringBuffer stringBuffer = new StringBuffer();
	
	private Symbol symbol(int type) {
		return symbol(type, yytext());
	}
	
	private Symbol symbol(int type, String typename, String value, String info) {
		if (type == SymbolsTable.EOF)
			System.out.printf ("Encontrado %s (%d), que é: %s\n", typename, type, info);
		else
			System.out.printf ("Encontrado `%s`, token do tipo %s (%d), que é: %s\n", value, typename, type, info);
		
		Object valueToCup = value;
		if (typename.equals("numinteger")) 
			valueToCup = Integer.valueOf(value);
		else if (typename.equals("numoctal"))
			valueToCup = Integer.parseInt(value.substring(0, value.length() - 2), 8);
		else if (typename.equals("numhexadecimal"))
			valueToCup = Integer.parseInt(value.substring(0, value.length() - 2), 16);
		else if (typename.equals("numreal"))
			valueToCup = Double.valueOf(value);
		
		return new Symbol(type, yyline, yycolumn, valueToCup);
	}
	
	private String ConfirmIfIsNumber() {
	
		String foundToken = yytext();
		
		Pattern pattern = Pattern.compile("[_a-zA-Z]");
		Matcher matcher = pattern.matcher(String.valueOf(yycharat(yylength())));
		
		if (matcher.find())
			throw new RuntimeException("Erro Lexico na linha "+ (yyline+1) +", coluna "+ (yychar+1) +": Identificador ou nome de procedimento inválido");
			
		return foundToken;
	}
	
	private Symbol symbol(int type, Object val) {
		return new Symbol(type, yyline, yycolumn, val);
	}

	public int getLine() {
		return yyline; 
	}

	public int getColumn() {
		return yycolumn;
	}

	public boolean hasNext() {
		return !zzAtEOF;
  	}
  	
	private void error(String message) {
		throw new RuntimeException("Erro Lexico na linha "+ (yyline+1) +", coluna "+ (yychar+1) +": " + message);
	}

	public String toString(){
		return yytext();
	}
	
	public Boolean scannerIsAtEof() {
		return this.zzAtEOF;
	}
	
%}


delim = [ \n\t\r]
whitespace = {delim}+
letter = [a-zA-Z]
octal_digit = [0-7]
digit = [0-9]
hexadecimal_additional_digits = [A-F]
numinteger = {digit}+
numreal = {numinteger}\.{numinteger}([E][+-]?{numinteger})?
numoctal = {octal_digit}+"B"
numhexadecimal = {digit}({digit}|{hexadecimal_additional_digits})+"H"
ident = {letter} ({letter} | {digit})*
strdelimiter = \'|\"
commentstart = (\(\*)
commentend = (\*\))


%state STRING_STATE_SIMPLE STRING_STATE_DOUBLE COMMENT_STATE

%%

<YYINITIAL> {
	{whitespace}    {  }
	    {delim}     {  }
	{commentstart}  { yybegin(COMMENT_STATE); }
	{strdelimiter}	{
						stringBuffer = new StringBuffer();
						if (yytext().charAt(0) == '"')
							yybegin(STRING_STATE_DOUBLE);
						else
							yybegin(STRING_STATE_SIMPLE);
					}
		"TRUE"		{ return symbol(SymbolsTable.TRUE,	"TRUE", yytext(), "Constante predefinida (TRUE)"); }
		"FALSE"		{ return symbol(SymbolsTable.FALSE,	"FALSE", yytext(), "Constante predefinida (FALSE)"); }
		"NIL"		{ return symbol(SymbolsTable.NIL,	"NIL", yytext(), "Constante predefinida (NIL)"); }
	    "AND"       { return symbol(SymbolsTable.AND,	"AND", yytext(), "Palavra reservada");          }
	    "STRING"    { return symbol(SymbolsTable.STRING,	"STRING", yytext(), "Palavra reservada");        }
	    "BOOLEAN"   { return symbol(SymbolsTable.BOOLEAN,	"BOOLEAN", yytext(), "Palavra reservada");        }
	    "INTEGER"   { return symbol(SymbolsTable.INTEGER,	"INTEGER", yytext(), "Palavra reservada");        }
	    "CHAR"      { return symbol(SymbolsTable.CHAR,	"CHAR", yytext(), "Palavra reservada");        }
	    "REAL"      { return symbol(SymbolsTable.REAL,	"REAL", yytext(), "Palavra reservada");        }
	    "LONGINT"	{ return symbol(SymbolsTable.LONGINT,	"LONGINT", yytext(), "Palavra reservada");        }
		"LONGREAL"	{ return symbol(SymbolsTable.LONGREAL,	"LONGREAL", yytext(), "Palavra reservada");        }
		"CARDINAL"	{ return symbol(SymbolsTable.CARDINAL,	"CARDINAL", yytext(), "Palavra reservada");        }
		"BITSET"	{ return symbol(SymbolsTable.BITSET,	"BITSET", yytext(), "Palavra reservada");        }
		"PROC"		{ return symbol(SymbolsTable.PROC,	"PROC", yytext(), "Palavra reservada");        }
	    "DIV"       { return symbol(SymbolsTable.DIV,	"DIV", yytext(), "Palavra reservada");          }
	    "MOD"       { return symbol(SymbolsTable.MOD,	"MOD", yytext(), "Palavra reservada");          }
	    "OR"        { return symbol(SymbolsTable.OR,	"OR", yytext(), "Palavra reservada"); 		    }
	    "NOT"       { return symbol(SymbolsTable.NOT,	"NOT", yytext(), "Palavra reservada");		    }
	    "IN"		{ return symbol(SymbolsTable.IN,	"IN", yytext(), "Palavra reservada");		    }
	    
	    "ARRAY"     { return symbol(SymbolsTable.ARRAY,	"ARRAY", yytext(), "Palavra reservada");        }
	    "BEGIN"     { return symbol(SymbolsTable.BEGIN,	"BEGIN", yytext(), "Palavra reservada");        } 
	    "BY"        { return symbol(SymbolsTable.BY,	"BY", yytext(), "Palavra reservada");           }
	    "CASE"      { return symbol(SymbolsTable.CASE,	"CASE", yytext(), "Palavra reservada");         }
	    "CONST"     { return symbol(SymbolsTable.CONST,	"CONST", yytext(), "Palavra reservada");        }
	    "DO"        { return symbol(SymbolsTable.DO,	"DO", yytext(), "Palavra reservada");           }
	    "ELSE"      { return symbol(SymbolsTable.ELSE,	"ELSE", yytext(), "Palavra reservada");         }
	    "ELSEIF"    { return symbol(SymbolsTable.ELSEIF,"ELSEIF", yytext(), "Palavra reservada");       }
	    "END"       { return symbol(SymbolsTable.END,	"END", yytext(), "Palavra reservada");          }
	    "EXIT"      { return symbol(SymbolsTable.EXIT,	"EXIT", yytext(), "Palavra reservada");         }
	    "FOR"       { return symbol(SymbolsTable.FOR,	"FOR", yytext(), "Palavra reservada");          }
	    "IF"        { return symbol(SymbolsTable.IF,	"IF", yytext(), "Palavra reservada");           }
	    "LOOP"      { return symbol(SymbolsTable.LOOP,	"LOOP", yytext(), "Palavra reservada");         }
	    "MODULE"    { return symbol(SymbolsTable.MODULE,"MODULE", yytext(), "Palavra reservada");       }
	    "OF"        { return symbol(SymbolsTable.OF,	"OF", yytext(), "Palavra reservada");           }
	    "PROCEDURE" { return symbol(SymbolsTable.PROCEDURE,	"PROCEDURE", yytext(), "Palavra reservada");}
	    "REPEAT"    { return symbol(SymbolsTable.REPEAT,	"REPEAT", yytext(), "Palavra reservada");   }
	    "RETURN"    { return symbol(SymbolsTable.RETURN,	"RETURN", yytext(), "Palavra reservada"); 	}
	    "THEN"      { return symbol(SymbolsTable.THEN,	"THEN", yytext(), "Palavra reservada");         }
	    "TO"        { return symbol(SymbolsTable.TO,	"TO", yytext(), "Palavra reservada");		    }
	    "UNTIL"     { return symbol(SymbolsTable.UNTIL,	"UNTIL", yytext(), "Palavra reservada"); 	    }
	    "VAR"       { return symbol(SymbolsTable.VAR,	"VAR", yytext(), "Palavra reservada"); 		    }
	    "WHILE"     { return symbol(SymbolsTable.WHILE,	"WHILE", yytext(), "Palavra reservada"); 	    }
	    "WITH"      { return symbol(SymbolsTable.WITH,	"WITH", yytext(), "Palavra reservada");		    }
	    
	    "~"			{ return symbol(SymbolsTable.NEGATION,	"NEGATION", yytext(), "~"); 	    }
	    ":"         { return symbol(SymbolsTable.COLON,	"COLON", yytext(), ":"); 	    }
	    ","         { return symbol(SymbolsTable.COMMA,	"COMMA", yytext(), ","); 	    }
	    ".."        { return symbol(SymbolsTable.DOUBLEDOT,	"DOUBLEDOT", yytext(), "..");    }
	    "="         { return symbol(SymbolsTable.EQ,	"EQ", yytext(), "=");		    }
	    "."         { return symbol(SymbolsTable.DOT,	"DOT", yytext(), ".");		    }
	    ";"         { return symbol(SymbolsTable.SEMICOLON,	"SEMICOLON", yytext(), ";");    }
	    "|"         { return symbol(SymbolsTable.PIPE,	"PIPE", yytext(), "|"); 		}
	    "("         { return symbol(SymbolsTable.LPARENTHESIS,	"LPARENTHESIS", yytext(), "("); }
	    ")"         { return symbol(SymbolsTable.RPARENTHESIS,	"RPARENTHESIS", yytext(), ")"); }
	    "["         { return symbol(SymbolsTable.LBRACKET,	"LBRACKET", yytext(), "[");     }
	    "]"         { return symbol(SymbolsTable.RBRACKET,	"RBRACKET", yytext(), "]");     }
	    "{"         { return symbol(SymbolsTable.LKEY,	"LKEY", yytext(), "{");     }
	    "}"         { return symbol(SymbolsTable.RKEY,	"RKEY", yytext(), "}");     }
	    ":="        { return symbol(SymbolsTable.ASSIGNMENT,	"ASSIGNMENT", yytext(), ":=");   }
	    "+"         { return symbol(SymbolsTable.PLUS,	"PLUS", yytext(), "+");         }
	    "-"         { return symbol(SymbolsTable.MINUS,	"MINUS", yytext(), "-");        }
	    "*"         { return symbol(SymbolsTable.TIMES,	"TIMES", yytext(), "*");        }
	    "^"         { return symbol(SymbolsTable.CIRC,	"CIRC", yytext(), "^");        }
	    "/"         { return symbol(SymbolsTable.DIVISION,	"DIVISION", yytext(), "/");     }
	    "&"         { return symbol(SymbolsTable.AND,	"AND", yytext(), "&");          }
	    "="         { return symbol(SymbolsTable.EQ,	"EQ", yytext(), "=");           }
	    "<"         { return symbol(SymbolsTable.LT,	"LT", yytext(), "<");           }
	    "<="        { return symbol(SymbolsTable.LE,	"LE", yytext(), "<=");           }
	    ">"         { return symbol(SymbolsTable.GT,	"GT", yytext(), ">");           }
	    ">="        { return symbol(SymbolsTable.GE,	"GE", yytext(), ">=");           }
	    "<>"        { return symbol(SymbolsTable.NE,	"NE", yytext(), "<>");           }
	    "#"         { return symbol(SymbolsTable.NE,	"NE", yytext(), "#");           }
	    
	    {numoctal}	{ String foundToken = ConfirmIfIsNumber(); return symbol(SymbolsTable.numoctal,	"numoctal", foundToken, "Literal (Octal)");      }
	    {numhexadecimal}	{ String foundToken = ConfirmIfIsNumber(); return symbol(SymbolsTable.numhexadecimal,	"numhexadecimal", foundToken, "Literal (Hexadecimal)");      }
		{numreal}	{ String foundToken = ConfirmIfIsNumber(); return symbol(SymbolsTable.numreal,	"numreal", foundToken, "Literal (Real)");      }
	    {numinteger} { String foundToken = ConfirmIfIsNumber(); return symbol(SymbolsTable.numinteger,	"numinteger", foundToken, "Literal (Integer)");   }
	    {ident}     { return symbol(SymbolsTable.IDENT,	"IDENT", yytext(), "Identificador");     }
		 
		<<EOF>> 	{ return symbol(SymbolsTable.EOF,	"EOF", yytext(), "Fim de arquivo"); 					}
}

<STRING_STATE_SIMPLE> {
		"\\\\"		{ stringBuffer.append( "\\" ); }
		"\\\'"		{ stringBuffer.append("'"); }
		[^\\\'] 	{ stringBuffer.append(yytext()); }
		[\"]		{ stringBuffer.append('"'); }
		
	{strdelimiter}	{
						yybegin(YYINITIAL);
						if (stringBuffer.length() == 1)
							return symbol(SymbolsTable.charvalue, "charvalue", stringBuffer.toString(), "Literal (Character)");
						return symbol(SymbolsTable.strvalue, "strvalue", stringBuffer.toString(), "Literal (String)");
	}
	<<EOF>>			{ throw new RuntimeException("Delimitador final de string ' não encontrado"); } 
}

<STRING_STATE_DOUBLE> {
		"\\\\"		{ stringBuffer.append( "\\" ); }
		"\\\""		{ stringBuffer.append('"'); }
		[^\\\"] 	{ stringBuffer.append(yytext()); }
		[\']		{ stringBuffer.append("'"); }
		
	{strdelimiter}	{
						yybegin(YYINITIAL);
						if (stringBuffer.length() == 1)
							return symbol(SymbolsTable.charvalue, "charvalue", stringBuffer.toString(), "Literal (Character)");
						return symbol(SymbolsTable.strvalue, "strvalue", stringBuffer.toString(), "Literal (String)");
	}
	<<EOF>>			{ throw new RuntimeException("Delimitador final de string \" não encontrado"); } 
}

<COMMENT_STATE> {
	{commentend}	{
						yybegin(YYINITIAL);
		}
		[^] 		{ }
	<<EOF>>			{ throw new RuntimeException("Delimitador final de comentário *) não encontrado"); } 
}


[^]  {System.err.println("Sequência inválida: '"+ yytext() +"'");}

